#include <stdlib.h>
#include <assert.h>
#include "hunter.h"

#include "globals.h"
#include "animation.h"
#include "source.h"
#include "point2.h"
#include "statectrl.h"
#include "level.h"
#include "audio.h"
#include "frame.h"
#include "rect.h"
#include "game.h"

Hunter::Hunter(){

	ani_stand =NULL;	
	ani_run =NULL;
	ani_jump =NULL;
	ani_fall =NULL;
	ani_aim_up =NULL;
	ani_aim_down =NULL;
	ani_shoot =NULL;
	ani_shoot_up =NULL;
	ani_shoot_down =NULL;
	ani_dead =NULL;
	ani_reload =NULL;

	src_shoot =NULL;
}

Hunter::~Hunter(){

	delete ani_stand;	
	ani_stand =NULL;	
	delete ani_run;
	ani_run =NULL;
	delete ani_jump;
	ani_jump =NULL;
	delete ani_fall;
	ani_fall =NULL;
	delete ani_aim_up;
	ani_aim_up =NULL;
	delete ani_aim_down;
	ani_aim_down =NULL;
	delete ani_shoot;
	ani_shoot =NULL;
	delete ani_shoot_up;
	ani_shoot_up =NULL;
	delete ani_shoot_down;
	ani_shoot_down =NULL;
	delete ani_dead;	
	ani_dead =NULL;	
	delete ani_reload;
	ani_reload =NULL;

	delete src_shoot;
	src_shoot =NULL;
}

void Hunter::setStill(int argument){
	
	if(argument==AIM_PREVIOUS)
		argument =this->aim;
	switch(argument){

	case AIM_STRAIGHT:
		cur_animation =ani_stand;
		break;
	case AIM_UP:
		cur_animation =ani_aim_up;
		break;
	case AIM_DOWN:
		cur_animation =ani_aim_down;
		break;
	}
	aim =argument;
	velocity->x =0;//?
	velocity->y =0;
	set_func =still;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_STILL;
}

void Hunter::setMove(int argument){

	cur_animation =ani_run;
	set_func =move;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_MOVE;
}

void Hunter::setJump(int argument){

	cur_animation =ani_jump;
	set_func =jump;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_JUMP;
}

void Hunter::setFall(int argument){

	cur_animation =ani_fall;
	set_func =fall;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_FALL;
}

void Hunter::setDead(int argument){

	cur_animation =ani_dead;
	velocity->x =0;
	velocity->y =die_jump;
	set_func =die;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_DEAD;
}

void Hunter::setAttack(int argument){

	if(argument==AIM_PREVIOUS)
		argument =this->aim;
	switch(argument){

	case AIM_STRAIGHT:
		cur_animation =ani_shoot;
		break;
	case AIM_UP:
		cur_animation =ani_shoot_up;
		break;
	case AIM_DOWN:
		cur_animation =ani_shoot_down;
		break;
	}
	set_func =shoot;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_ATTACK;
}

void Hunter::setReload(int argument){

	cur_animation =ani_reload;
	set_func =reload;

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =STATE_RELOAD;
}

/*
void Hunter::setState(void (Hunter::*setStill)(int), int argument){
	
	switch(state){
	case STATE_STILL:
		if(argument==AIM_PREVIOUS)
			argument =this->aim;
		switch(argument){

		case AIM_STRAIGHT:
			cur_animation =ani_stand;
			break;
		case AIM_UP:
			cur_animation =ani_aim_up;
			break;
		case AIM_DOWN:
			cur_animation =ani_aim_down;
			break;
		}
		aim =argument;
		velocity->x =0;//?
		velocity->y =0;
		set_func =still;
		break;
	case STATE_MOVE:
		cur_animation =ani_run;
		set_func =move;
		break;
	case STATE_JUMP:
		cur_animation =ani_jump;
		set_func =jump;
		break;
	case STATE_FALL:
		cur_animation =ani_fall;
		set_func =fall;
		break;
	case STATE_DEAD:
		cur_animation =ani_dead;
		velocity->x =0;
		velocity->y =die_jump;
		set_func =die;
		break;
	case HUNTER_STATE_RELOAD:
		cur_animation =ani_reload;
		set_func =reload;
		break;
	case STATE_ATTACK:
		if(argument==AIM_PREVIOUS)
			argument =this->aim;
		switch(argument){

		case AIM_STRAIGHT:
			cur_animation =ani_shoot;
			break;
		case AIM_UP:
			cur_animation =ani_shoot_up;
			break;
		case AIM_DOWN:
			cur_animation =ani_shoot_down;
			break;
		}
		set_func =shoot;
		break;
	}

	cur_animation->begin_time =0;
	cur_animation->cur_frame =0;
	new_frame =true;
	this->state =state;
}
*/

void Hunter::update(Level* level, Point2* acc, float dt){

	int passed_time;
	int passed_frames;
	div_t div_tmp;
	unsigned int fr1;

	// update animation
	fr1 =cur_animation->cur_frame;

	cur_animation->begin_time +=dt;
	passed_time =cur_animation->begin_time;
	passed_frames =int((float)passed_time/(1000.0f/cur_animation->fps));
	div_tmp =div(passed_frames, cur_animation->nFrames);
	cur_animation->cur_frame =div_tmp.rem;

	(this->*set_func)(level, acc, dt);
/*
	switch(state){
	case HUNTER_STATE_RELOAD:
		reload(level, acc, dt);
		break;
	case STATE_ATTACK:
		shoot(level, acc, dt);
		break;
	case STATE_STILL:
		still(level, acc, dt);
		break;
	case STATE_MOVE:
		move(level, acc, dt);
		break;
	case STATE_FALL:
		fall(level, acc, dt);
		break;
	case STATE_JUMP:
		jump(level, acc, dt);
		break;
	case STATE_DEAD:
		die(level, acc, dt);
		break;
	}
*/
	// if the frame has changed or if it is a first frame (no frame predates)
	if(cur_animation->cur_frame!=fr1 || new_frame){
			srcPlay(this); 
	}
	new_frame =false;
}

void Hunter::still(Level* level, Point2* acc, float dt){

	Audio* audio =Audio::Instance();
	Game* game =Game::Instance();
	// check if I am not falling
	genStill(level, this);

	if(special2 && ammo <max_ammo){

//		setState(HUNTER_STATE_RELOAD, 0);
		setReload(0);
		return;
	}

	if(moveYplus){
		if(aim!=AIM_UP)
//			setState(STATE_STILL, AIM_UP);
			setStill(AIM_UP);
	}
	else
	if(moveYminus){
		if(aim!=AIM_DOWN)
//			setState(STATE_STILL, AIM_DOWN);
			setStill(AIM_DOWN);
	}
	else{
		if(aim!=AIM_STRAIGHT)
//			setState(STATE_STILL, AIM_STRAIGHT);
			setStill(AIM_STRAIGHT);

		if(moveXplus){
			velocity->x =speed;
			orientation->x =1;
//			setState(STATE_MOVE, 0);
			setMove(0);
			return;
		}
		else
		if(moveXminus){
			velocity->x =-speed;	
			orientation->x =-1;
//			setState(STATE_MOVE, 0);
			setMove(0);
			return;
		}
	}

	if(special && game->jump_ready){
		game->jump_ready =false;
		velocity->y =jump_speed;
//		setState(STATE_JUMP, 0);
		setJump(0);
		return;
	}
	else
	if(attack){
		if(ammo>0){
//			setState(STATE_ATTACK, aim);
			setAttack(aim);
			genShoot(level, this);
		}
		else{
			if(!audio->isPlaying(SRC_NOAMMO->id)){
				audio->setSource(SRC_NOAMMO->id, SRC_NOAMMO->buffer, position->x, position->y, SRC_NOAMMO->z, SRC_NOAMMO->gain, SRC_NOAMMO->loop);
				audio->playSource(SRC_NOAMMO->id);
			}
		}
		return;
	}
}

void Hunter::shoot(Level* level, Point2* acc, float dt){

	// check if I am not falling
	genStill(level, this);

	if(moveYplus){
		aim =AIM_UP;
	}
	else
	if(moveYminus){
		aim =AIM_DOWN;
	}
	else{
		aim =AIM_STRAIGHT;
	}

	if(moveXminus)
		orientation->x =-1;
	else
	if(moveXplus)
		orientation->x =1;

	// at the end of animation - switch back to STATE_STILL
	int passed_time =cur_animation->begin_time;
	if(passed_time>=cur_animation->nFrames*(1000.0f/cur_animation->fps)){
//		setState(STATE_STILL, AIM_PREVIOUS);
		setStill(AIM_PREVIOUS);
		return;
	}
}

void Hunter::move(Level* level, Point2* acc, float dt){

	Game* game =Game::Instance();

	if(moveYplus){
//		setState(STATE_STILL, AIM_UP);
		setStill(AIM_UP);
	}
	else if(moveYminus){
//		setState(STATE_STILL, AIM_DOWN);
		setStill(AIM_DOWN);
	}
	else{

		if(special && game->jump_ready){
			game->jump_ready =false;
			velocity->y =jump_speed;
//			setState(STATE_JUMP, 0);
			setJump(0);
			return;
		}
		if(moveXplus){
			velocity->x =speed;
			orientation->x =1;
		}
		else if(moveXminus){
			velocity->x =-speed;	
			orientation->x =-1;
		}
		else{
			velocity->x =0;
//			setState(STATE_STILL, AIM_STRAIGHT);
			setStill(AIM_STRAIGHT);
		}
	}

	genMove(level, this, dt);
}

void Hunter::jump(Level* level, Point2* acc, float dt){

	// find velocity vector
	if(special)
		*velocity += *acc*(dt*jump_mult*0.001f);
	else
		*velocity += *acc*(dt*0.001f);
	if(moveXplus){
		velocity->x =speed;
		orientation->x =1;
	}
	else if(moveXminus){
		velocity->x =-speed;	
		orientation->x =-1;
	}
	else {
		velocity->x =0;
	}

	genJump(level, this, dt);
}

void Hunter::fall(Level* level, Point2* acc, float dt){

	if(special)
		special =false;
	if(moveXplus){
		velocity->x =speed;
		orientation->x =1;
	}
	else if(moveXminus){
		velocity->x =-speed;	
		orientation->x =-1;
	}
	else{
		velocity->x =0;
	}

	genFall(level, this, acc, dt);
}

void Hunter::die(Level* level, Point2* acc, float dt){

	// find velocity vector
	*velocity += *acc*(dt*0.001f);
	// find translation vector
	*position +=*velocity*dt*0.1f; // 100cm/1000ms
}

void Hunter::reload(Level* level, Point2* acc, float dt){

	int passed_time =cur_animation->begin_time;
	if(passed_time>=cur_animation->nFrames*(1000.0f/cur_animation->fps)){
		ammo++;
//		setState(STATE_STILL, AIM_DOWN);
		setStill(AIM_DOWN);
	}
}

float Hunter::get_xLo(){

	return position->x + cur_animation->frames[cur_animation->cur_frame]->body->xLo;
}

float Hunter::get_xHi(){

	return position->x + cur_animation->frames[cur_animation->cur_frame]->body->xHi;
}

float Hunter::get_yHi(){

	return position->y + cur_animation->frames[cur_animation->cur_frame]->body->yHi;
}

float Hunter::get_yLo(){

	return position->y + cur_animation->frames[cur_animation->cur_frame]->body->yLo;
}

float Hunter::get_pic_xLo(){

	return position->x + cur_animation->frames[cur_animation->cur_frame]->box->xLo;
}

float Hunter::get_pic_xHi(){

	return position->x + cur_animation->frames[cur_animation->cur_frame]->box->xHi;
}

float Hunter::get_pic_yHi(){

	return position->y + cur_animation->frames[cur_animation->cur_frame]->box->yHi;
}

float Hunter::get_pic_yLo(){

	return position->y + cur_animation->frames[cur_animation->cur_frame]->box->yLo;
}

Rect* Hunter::getBody(){

	return cur_animation->frames[cur_animation->cur_frame]->body;
}

Rect* Hunter::getBox(){

	return cur_animation->frames[cur_animation->cur_frame]->box;
}
